package com.zch.rpc.core.net.jetty2;

import com.zch.rpc.core.exception.RpcException;
import com.zch.rpc.core.model.RpcRequestModel;
import com.zch.rpc.core.model.RpcResponseModel;
import com.zch.rpc.core.provider.RpcProviderFactory;
import com.zch.rpc.core.serialize.Serializer;
import com.zch.rpc.core.serialize.impl.HessianSerializer;
import com.zch.rpc.core.util.ThrowableUtil;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @Auther: zch
 * @Date: 2018/12/28 13:50
 * @Description:
 */
public class JettyServerHandler extends AbstractHandler {
    private static Logger logger = LoggerFactory.getLogger(JettyServerHandler.class);

    /**
     * write response
     */
    private void writeResponse(Request baseRequest, HttpServletResponse response, byte[] responseBytes) throws IOException {

        response.setContentType("text/html;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_OK);
        baseRequest.setHandled(true);

        OutputStream out = response.getOutputStream();
        out.write(responseBytes);
        out.flush();
    }

    /**
     * parse request
     */
    private RpcRequestModel parseRequest(HttpServletRequest request) throws Exception {
        // deserialize request
        byte[] requestBytes = readBytes(request);
        if (requestBytes == null || requestBytes.length==0) {
            throw new RpcException("rpc request data is empty.");
        }
        HessianSerializer hessianSerializer=new HessianSerializer();
        RpcRequestModel requestModel = (RpcRequestModel) hessianSerializer.deserialize(requestBytes, RpcRequestModel.class);
        return requestModel;
    }

    /**
     * read bytes from http request
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static final byte[] readBytes(HttpServletRequest request) throws IOException {
        request.setCharacterEncoding("UTF-8");
        int contentLen = request.getContentLength();
        InputStream is = request.getInputStream();
        if (contentLen > 0) {
            int readLen = 0;
            int readLengthThisTime = 0;
            byte[] message = new byte[contentLen];
            try {
                while (readLen != contentLen) {
                    readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                    if (readLengthThisTime == -1) {
                        break;
                    }
                    readLen += readLengthThisTime;
                }
                return message;
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
        return new byte[] {};
    }

    @Override
    public void handle(String url, Request baseRequest, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {
        if ("/services".equals(url)) {	// services mapping
            writeResponse(baseRequest, httpServletResponse, "waiting".toString().getBytes());
            return;
        } else {	// default remoting mapping
            // request parse
            RpcRequestModel requestModel = null;
            try {

                requestModel = parseRequest(httpServletRequest);
            } catch (Exception e) {
                writeResponse(baseRequest, httpServletResponse, ThrowableUtil.toString(e).getBytes());
                return;
            }

            // invoke request请求
            RpcResponseModel responseModel= RpcProviderFactory.invokerService(requestModel);
            responseModel.setRequestId(requestModel.getRequestId());
            Serializer hessianSerializer=Serializer.SerializeEnum.HESSIAN.getSerializer();

            byte[] responseBytes =hessianSerializer.serialize(responseModel) ;
            writeResponse(baseRequest, httpServletResponse, responseBytes);
        }

    }
}
